package cz.vse.po1715.lode.net.klient;

import cz.vse.po1715.lode.net.obecne.FrontaUdálostí;
import cz.vse.po1715.lode.net.obecne.IHerniUdalost;
import cz.vse.po1715.lode.net.obecne.Příloha;
import java.nio.*;
import java.nio.channels.*;
import java.util.*;
import java.io.*;
import java.util.logging.*;

/*******************************************************************************
 * Instance třídy {@code NIONačítačUdálostí} představují ...
 *
 * @author    Dominik KOSTKA
 * @version   0.00.000
 */
public class NIONačítačUdálostí extends Thread
{
//== KONSTANTNÍ ATRIBUTY TŘÍDY =================================================
//== PROMĚNNÉ ATRIBUTY TŘÍDY ===================================================
//== STATICKÝ INICIALIZAČNÍ BLOK - STATICKÝ KONSTRUKTOR ========================
//== KONSTANTNÍ ATRIBUTY INSTANCÍ ==============================================
//== PROMĚNNÉ ATRIBUTY INSTANCÍ ================================================
    /** log4j logger */
    private Logger log = Logger.getLogger("NIONačítačUdálostí");

    /** odkaz na klienta */
    AKlient klient;

    /** připojení k serveru */
    private SocketChannel kanál;

    /** fronta pro příchozí události */
    private FrontaUdálostí fronta;

    /** sselector pro řízení připojení k serveru */
    private Selector selector;

    /** příznak běhu */
    private  boolean běžící;
//== PŘÍSTUPOVÉ METODY VLASTNOSTÍ TŘÍDY ========================================
//== OSTATNÍ NESOUKROMÉ METODY TŘÍDY ===========================================

//##############################################################################
//== KONSTRUKTORY A TOVÁRNÍ METODY =============================================

    /***************************************************************************
     *
     */
    public NIONačítačUdálostí(AKlient klient, SocketChannel kanál, 
            FrontaUdálostí fronta)
    {
        super("NIONačítačUdálostí");
	this.klient = klient;
	this.fronta = fronta;
	this.kanál = kanál;
    }



//== ABSTRAKTNÍ METODY =========================================================
//== PŘÍSTUPOVÉ METODY VLASTNOSTÍ INSTANCÍ =====================================

    /**
     * toto je téměř shodné s metodou SelectAndRead.select()
     */
    @Override
    public void run() {
	try {
	    selector = Selector.open();
	    kanál.register(selector, SelectionKey.OP_READ, new Příloha());
	}
	catch (ClosedChannelException cce) {
	    log.info("closedchannelexception během registrace kanálu se " +
                    "selectorem");
	    return;
	}
	catch (IOException ioe) {
	    log.info("ioexception během registrace kanálu se " +
                    "selectorem");
	    return;
	}

	běžící = true;
	while (běžící) {
	    try {
		selector.select();
		Set připravenéKlíče = selector.selectedKeys();

		Iterator i = připravenéKlíče.iterator();
		while (i.hasNext()) {
		    SelectionKey key = (SelectionKey) i.next();
		    i.remove();

		    SocketChannel kl = (SocketChannel) key.channel();
		    Příloha příloha = (Příloha) key.attachment();

		    try {
			long nbytes = kl.read(příloha.readBuff);
			if (nbytes == -1) {
			    kl.close();

			    IHerniUdalost událost = klient.
                                    vytvořOdpojovacíUdálost("end-of-stream");
			    fronta.pridej(událost);
			}

			try {
			    if (příloha.readBuff.position() >=
                                    Příloha.VELIKOST_HLAVIČKY) {
				příloha.readBuff.flip();

				while(příloha.událostPřipravena()) {
				IHerniUdalost událost = getUdálost(příloha);
				fronta.pridej(událost);
				příloha.vymaž();
				}
				příloha.readBuff.compact();
			    }
			}
			catch (IllegalArgumentException e) {
			    log.info("illegalargument během analyzování příchozí " +
                                    "události");
			}
		    }
		    catch (IOException ioe) {
			log.warning("IOException během read(), uzavírání kanálu:" + kl.socket().getInetAddress());
			kl.close();
		    }
		}
	    }
	    catch (IOException ioe2) {
		log.warning("chyba během select(): " + ioe2.getMessage());
	    }
	    catch (Exception e) {
		log.info("výjimka během select()");
	    }
	}
    }

    public void vypni() {
	běžící = false;
	// donutí selector k odblokování
	selector.wakeup();
    }
    
//== OSTATNÍ NESOUKROMÉ METODY INSTANCÍ ========================================
//== SOUKROMÉ A POMOCNÉ METODY TŘÍDY ===========================================
//== SOUKROMÉ A POMOCNÉ METODY INSTANCÍ ========================================

   private IHerniUdalost getUdálost(Příloha příloha) {
	ByteBuffer bb = ByteBuffer.wrap(příloha.dataUdálosti);

	// řekne klientovi, aby vytvořil instanci události
	IHerniUdalost událost = klient.vytvořHerníUdálost();
	událost.načti(bb);

	return událost;
    }

//== INTERNÍ DATOVÉ TYPY =======================================================
//== TESTOVACÍ METODY A TŘÍDY ==================================================
//
//    /***************************************************************************
//     * Testovací metoda.
//     */
//    public static void test()
//    {
//        NIONačítačUdálostí inst = new NIONačítačUdálostí();
//    }
//    /** @param args Parametry příkazového řádku - nepoužívané. */
//    public static void main( String[] args )  {  test();  }
}
